/*********************************************************************
 * Copyright (C) 2002 Andrew Khan
 * <p>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 ***************************************************************************/

package jxl.write.biff;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * The list of available shared strings.  This class contains
 * the labels used for the entire spreadsheet
 */
class SharedStrings {
    /**
     * All the strings in the spreadsheet, keyed on the string itself
     */
    private HashMap strings;

    /**
     * Contains the same strings, held in a list
     */
    private ArrayList stringList;

    /**
     * The total occurrence of strings in the workbook
     */
    private int totalOccurrences;

    /**
     * Constructor
     */
    public SharedStrings() {
        strings = new HashMap(100);
        stringList = new ArrayList(100);
        totalOccurrences = 0;
    }

    /**
     * Gets the index for the string passed in.  If the string is already
     * present, then returns the index of that string, otherwise
     * creates a new key-index mapping
     *
     * @param s the string whose index we want
     * @return the index of the string
     */
    public int getIndex(String s) {
        Integer i = (Integer) strings.get(s);

        if (i == null) {
            i = new Integer(strings.size());
            strings.put(s, i);
            stringList.add(s);
        }

        totalOccurrences++;

        return i.intValue();
    }

    /**
     * Gets the string at the specified index
     *
     * @param i the index of the string
     * @return the string at the specified index
     */
    public String get(int i) {
        return (String) stringList.get(i);
    }

    /**
     * Writes out the shared string table
     *
     * @param outputFile the binary output file
     * @exception IOException
     */
    public void write(File outputFile) throws IOException {
        // Thanks to Guenther for contributing the ExtSST implementation portion
        // of this method
        int charsLeft = 0;
        String curString = null;
        SSTRecord sst = new SSTRecord(totalOccurrences, stringList.size());
        ExtendedSSTRecord extsst = new ExtendedSSTRecord(stringList.size());
        int bucketSize = extsst.getNumberOfStringsPerBucket();

        Iterator i = stringList.iterator();
        int stringIndex = 0;
        while (i.hasNext() && charsLeft == 0) {
            curString = (String) i.next();
            // offset + header bytes
            int relativePosition = sst.getOffset() + 4;
            charsLeft = sst.add(curString);
            if ((stringIndex % bucketSize) == 0) {
                extsst.addString(outputFile.getPos(), relativePosition);
            }
            stringIndex++;
        }
        outputFile.write(sst);

        if (charsLeft != 0 || i.hasNext()) {
            // Add the remainder of the string to the continue record
            SSTContinueRecord cont = createContinueRecord(curString,
                    charsLeft,
                    outputFile);

            // Carry on looping through the array until all the strings are done
            while (i.hasNext()) {
                curString = (String) i.next();
                int relativePosition = cont.getOffset() + 4;
                charsLeft = cont.add(curString);
                if ((stringIndex % bucketSize) == 0) {
                    extsst.addString(outputFile.getPos(), relativePosition);
                }
                stringIndex++;

                if (charsLeft != 0) {
                    outputFile.write(cont);
                    cont = createContinueRecord(curString, charsLeft, outputFile);
                }
            }

            outputFile.write(cont);
        }

        outputFile.write(extsst);
    }

    /**
     * Creates and returns a continue record using the left over bits and
     * pieces
     */
    private SSTContinueRecord createContinueRecord
    (String curString, int charsLeft, File outputFile) throws IOException {
        // Set up the remainder of the string in the continue record
        SSTContinueRecord cont = null;
        while (charsLeft != 0) {
            cont = new SSTContinueRecord();

            if (charsLeft == curString.length() || curString.length() == 0) {
                charsLeft = cont.setFirstString(curString, true);
            } else {
                charsLeft = cont.setFirstString
                        (curString.substring(curString.length() - charsLeft), false);
            }

            if (charsLeft != 0) {
                outputFile.write(cont);
                cont = new SSTContinueRecord();
            }
        }

        return cont;
    }
}
